#pragma once

#include <etcd/Client.hpp>
#include <etcd/KeepAlive.hpp>
#include <etcd/Response.hpp>
#include <etcd/Value.hpp>
#include <etcd/Watcher.hpp>
#include <functional>
#include "logger.hpp"



/*
    Etcd 是一个 golang 编写的分布式、高可用的一致性键值存储系统，用于配置共享和服务发现等。它使用 Raft 一致性算法来保持集群数据的一致性，且客户端通过长连接
    watch 功能，能够及时收到数据变化通知，相较于 Zookeeper 框架更加轻量化。

*/


namespace myChatSystem
{

    // 服务注册客户端
    class Registry
    {
    public:
        using ptr = shared_ptr<Registry>;
        Registry(const std::string &host)
            :Client_(std::make_shared<etcd::Client>(host))
            ,KeepAlive_(Client_->leasekeepalive(3).get())
            ,leaseId_(KeepAlive_->Lease())
        {}

        ~Registry(){KeepAlive_->Cancel();}

        // 注册服务
        bool registry(const std::string& key,const std::string& val)
        {
            auto res = Client_->put(key,val,leaseId_).get();
            if(res.is_ok() == false)
            {
                LOG_ERROR("注册数据失败：{}", res.error_message());
                return false;
            }
            return true;
        }
    private:
        std::shared_ptr<etcd::Client> Client_;
        std::shared_ptr<etcd::KeepAlive> KeepAlive_;
        uint64_t leaseId_;                                  // 租约id
    };


    // 服务发现客户端
    class Discovery
    {
    public:
        using ptr = std::shared_ptr<Discovery>;
        using NotifyCallBack = std::function<void(std::string ,std::string)>;
        Discovery(const std::string &host,const std::string &basedir,NotifyCallBack put_cb,NotifyCallBack del_cb)
            :Client_(std::make_shared<etcd::Client>(host))
            ,put_cb_(put_cb)
            ,del_cb_(del_cb)
        {
            // 先进行服务发现,先获取到当前已有的数据
            auto res = Client_->ls(basedir).get();
            if(res.is_ok() == false)
            {
                LOG_DEBUG("获取服务信息失败：{}",res.error_message());
            }

            int size = res.keys().size();           // 已经上线的服务对应的key的数量
            for(int i = 0;i<size;++i)
            {
                if(put_cb_)
                    put_cb_(res.key(i),res.value(i).as_string());       // 将已经上线的服务返回
            }

            // 然后进行事件监控，监控数据发生的改变并调用回调进行处理
            Watcher_ = std::make_shared<etcd::Watcher>(*Client_.get(),basedir,std::bind(&Discovery::callback,this,std::placeholders::_1),true);
        }

        ~Discovery(){ Watcher_->Cancel();}
        

        void startWatch()
        {
            Watcher_->Wait();           // 等待事件通知
        }
    private:
        // 新服务上线的回调函数
        void callback(const etcd::Response &resp)
        {
        
            if(resp.is_ok() == false)
            {
                LOG_ERROR("收到一个错误的事件通知: {}", resp.error_message());
                return;
            }


            for(auto &ev : resp.events())
            {
                if(ev.event_type() == etcd::Event::EventType::PUT)
                {
                    put_cb_(ev.kv().key(),ev.kv().as_string());
                    LOG_DEBUG("服务上线: {} - {}", ev.kv().key(), ev.kv().as_string());
                }
                else if(ev.event_type() == etcd::Event::EventType::DELETE_)
                {
                    del_cb_(ev.kv().key(),ev.kv().as_string());
                    LOG_DEBUG("服务下线: {} - {}", ev.kv().key(), ev.kv().as_string());
                }
            }
        }

        NotifyCallBack  put_cb_;
        NotifyCallBack  del_cb_;
        std::shared_ptr<etcd::Client> Client_;                  // 客户端对象
        std::shared_ptr<etcd::Watcher> Watcher_;                // 监听者对象
    };

}